iT邦幫忙

2026 iThome 鐵人賽

DAY 8
0
Software Development

從 Vibe Coding 到可維護專案:用 Hermes Agent 重讀 RoomRush Android 專題系列 第 8 篇

Day 8 | 點進一間教室後,畫面背後其實查了哪些資料?

  • 分享至 

  • xImage
  •  

前言:點進一間教室後,畫面背後其實查了哪些資料?

前一篇文章介紹了 EmptyRoomAdapter.kt,我們理解了 Adapter 如何把 emptyRooms 轉化為帶有樓層標題的 RecyclerView 列表。然而,光是顯示列表還不夠,當使用者點擊某個教室時,系統該如何將他帶入下一頁的「教室詳細頁」?

今天要讀的兩個檔案—RoomDetailActivity.kt 與 RoomDetailViewModel.kt,正是來解決這個問題。

值得一提的是,這兩個檔案在職責分工上,與 Day 5、Day 6 所介紹的 QueryResultActivity 及 QueryResultViewModel 有著異曲同工之妙,完美貫徹了 View 與 ViewModel 的架構邊界。


一開始我認為

當初決定設計這個詳細頁功能時,想說如果可以點選每個教室的詳細資訊,會讓使用者能更直覺應用,整個 App 也很有互動感。

一開始我在想,依據查詢頁(QueryResult)設計成 View 與 ViewModel 的架構,或許詳細頁(RoomDetail)這部分,也是需要這樣的職責分離,這點果然不出所料。

不過同時在 UI 的設計上,我們也有點不確定,如果單純只有一個頁面,要怎麼接收單一的教室資訊?這點在我和 Hermes Agent 討論後,漸漸清楚裡面的技術細節。

UI 畫面 操作流程
UI 畫面 操作流程

實際讀完後,RoomDetailActivity.kt 及 RoomDetailViewModel.kt負責什麼

和 Hermes Agent 一重讀檔案後,我了解了這兩個檔案的職責:

  • RoomDetailActivity.kt(UI 控制器):負責接收上一頁傳來的教室名稱,觀察 ViewModel 查到的教室資料,並把教室名稱、教室類型、是否可飲食顯示到 room_detail.xml 畫面上。
  • RoomDetailViewModel.kt(資料邏輯中心):根據教室名稱(classroomName ),透過 Repository 取得該教室的教室資料(ClassroomSchedule)。

承接上個小標,我找到了當時解答接收單一的教室資訊的技術細節:

  1. 從 Intent 取得教室名稱
val classroomName = intent.getStringExtra(EXTRA_CLASSROOM_NAME)

if (classroomName == null) {
    finish()
    return
}
  • 技術細節: 在 EmptyRoomAdapter 觸發教室點擊事件後,QueryResultActivity 取得被點擊的教室名稱,並透過 Intent 的 Extra 傳給 RoomDetailActivity。RoomDetailActivity 再使用 getStringExtra(EXTRA_CLASSROOM_NAME) 取得這個名稱。
  • 防呆機制:如果因為任何異常導致上一頁沒有傳教室名稱,這一頁因為不知道要顯示什麼,會選擇直接 finish() 關閉頁面返回,避免 App 載入空資料造成閃退。
  1. 交給 ViewModel 向 Repository 撈取資料

Activity 拿到教室名稱後,不直接碰資料庫,而是交給 RoomDetailViewModel 處理:

// ViewModel 透過建構子注入 Repository
class RoomDetailViewModel(
    private val repository: ClassroomScheduleRepository
) : ViewModel() {

    // 將 Repository 的資料轉為 LiveData 供 Activity 觀察
    fun getDetails(classroomName: String): LiveData<ClassroomSchedule?> {
        return repository.getClassroomDetails(classroomName).asLiveData()
    }
}
  • 技術細節: RoomDetailViewModel 不直接操作資料庫,而是透過 Repository 取得教室資料,再使用 .asLiveData() 將資料流轉換成 LiveData,供 Activity 觀察。
  • 補充: 因為 ViewModel 建構子需要 Repository,因此建立 ViewModel 時會透過 Factory 將 Repository 傳入。

.asLiveData() 的小流程:

Repository
    ↓
Flow<ClassroomSchedule?>
    ↓ asLiveData()
LiveData<ClassroomSchedule?>
    ↓
Activity observe
  1. 觀察 ViewModel 查到的資料
private fun observeClassroomDetails(name: String) {
    viewModel.getDetails(name).observe(this, Observer { schedule ->
        if (schedule != null) {
            updateUi(schedule)
        }
    })
}
  • 技術細節: Activity 只需要「觀察(Observe)」資料變化,當資料非空值時才呼叫 updateUi() 更新畫面。
  1. 資料轉譯與更新 UI
private fun updateUi(schedule: ClassroomSchedule) {
    classroomNameTextView.text = schedule.classroom

    classroomTypeValueTextView.text = when (schedule.classroomType) {
        "Normal" -> "一般教室"
        "Computer" -> "電腦教室"
        "Drawing" -> "製圖教室"
        "Lab" -> "實驗室"
        "MeetingRoom" -> "研討室"
        else -> "未知"
    }

    foodAllowedValueTextView.text = if (schedule.classroomType == "Normal") "是" else "否"
}
  • 技術細節:將資料庫裡的英文代碼轉譯成中文字,以及加上邏輯判斷(根據目前系統的規則,只有 classroomType 為 Normal 的一般教室顯示為「可飲食」,其他類型則顯示「否」)。讓冰冷的數據變成使用者直覺可讀的畫面。

它接在哪一條流程上

先來看 RoomDetailActivity.kt 的流程,這裡的重點放在「接收教室 → 建立 ViewModel → 觀察資料 → 更新畫面」:

QueryResultActivity
    ↓
傳遞 classroomName
    ↓
RoomDetailActivity
    ↓
取得 classroomName
    ↓
呼叫 RoomDetailViewModel
    ↓
Observe ClassroomSchedule
    ↓
updateUi() 顯示教室詳細資訊

RoomDetailViewModel.kt 的流程,重點放在「拿到 classroomName 後,如何取得資料」:

RoomDetailActivity
    ↓
傳入 classroomName
    ↓
RoomDetailViewModel
    ↓
呼叫 Repository
    ↓
取得課表資料流
    ↓
轉換為 LiveData
    ↓
回傳 ClassroomSchedule

Hermes Agent 幫我檢查出的重點

RoomDetailActivity 是教室詳細頁的 UI 控制器。

  • 它從 Intent 中取得上一頁傳來的 classroomName,如果沒有取得教室名稱,就直接 finish() 關閉頁面。
  • 取得教室名稱後,它呼叫 viewModel.getDetails(name) 並觀察回傳的資料。
  • 當查到 ClassroomSchedule 後,updateUi() 會顯示教室名稱,將 classroomType 轉成中文,並依照是否為 Normal 判斷能否飲食。
  • 這個 Activity 不直接查資料庫,而是透過 ViewModel / Repository 的資料流取得資料。

RoomDetailViewModel 是教室詳細頁的資料邏輯中心。

  • 它需要 ClassroomScheduleRepository,因為 ViewModel 不直接查 DAO 或 Room Database,而是透過 Repository 取得資料。
  • getDetails(classroomName) 會把教室名稱交給 Repository 查詢該教室的 ClassroomSchedule,並用 asLiveData() 將 Repository 回傳的資料流轉成 LiveData<ClassroomSchedule?>,讓 RoomDetailActivity 可以 observe。
  • 由於這個 ViewModel 需要 Repository 參數,所以使用 RoomDetailViewModelFactory 來建立。

小結

讀到 RoomDetailActivity 和 RoomDetailViewModel 後,我看到 RoomRush 查詢流程的最後一步:使用者不是只看到空教室列表,還可以點進某一間教室看詳細資訊。

Activity 不直接查資料庫,它負責接收資料、觀察 ViewModel、更新畫面,但不直接查資料庫。教室類型從英文代碼轉成中文,是否可飲食也在 updateUi() 裡轉成使用者看得懂的文字。

ViewModel 也不直接碰 DAO,而是透過 Repository 取得資料。Repository 回傳的資料流再用 asLiveData() 轉成 Activity 可以觀察的形式。

用各一句話來總結這兩份檔案:

RoomDetailActivity 從 Intent 取得被點擊的教室名稱,觀察 ViewModel 查到的資料,再用 updateUi() 顯示教室名稱、類型與是否可飲食。

RoomDetailViewModel 不顯示畫面,只負責根據教室名稱(classroomName )透過 Repository 取得教室資料,並轉成 LiveData 給 Activity 觀察。


下一篇預告:我終於搞懂:在這個專案裡,X 不是空教室

到目前為止,我們已經把 RoomRush 從首頁到詳細頁的查詢流程大致串起來了。然而下一篇,我想要倒回去QueryResultViewModel.kt 裡,我們有說明,程式會判斷某間教室在指定時段是不是可以使用,判斷的其中一個條件,是看課表資料裡的欄位是不是:『X』。

我想要深入探討這細節,因為當初我在和 Hermes Agent 一起重讀這個部份時,我很容易搞混。如果這個『X』的邏輯判斷寫錯,整套空教室演算法算出來的結果就會完全顛倒。


上一篇
Day 7 | 查到空教室之後,App 怎麼把結果整理給使用者看?
下一篇
Day 9 | 我終於搞懂:在這個專案裡,X 不是空教室
系列文
從 Vibe Coding 到可維護專案:用 Hermes Agent 重讀 RoomRush Android 專題 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言